/* Copyright (c) 2020 vesoft inc. All rights reserved.
 *
 * This source code is licensed under Apache 2.0 License,
 * attached with Common Clause Condition 1.0, found in the LICENSES directory.
 */
#ifndef NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_
#define NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_

#include "graph/util/SchemaUtil.h"

namespace nebula {

class Expression;

namespace meta {
namespace cpp2 {
class ColumnDef;
class IndexItem;
}  // namespace cpp2
}  // namespace meta

namespace storage {
namespace cpp2 {
class IndexQueryContext;
}  // namespace cpp2
}  // namespace storage

namespace graph {

class IndexScan;

class OptimizerUtils {
 public:
  enum class BoundValueOperator {
    GREATER_THAN = 0,
    LESS_THAN,
    MAX,
    MIN,
  };

  OptimizerUtils() = delete;

  static Value boundValue(const meta::cpp2::ColumnDef& col,
                          BoundValueOperator op,
                          const Value& v = Value());

  static Value boundValueWithGT(const meta::cpp2::ColumnDef& col, const Value& v);

  static Value boundValueWithLT(const meta::cpp2::ColumnDef& col, const Value& v);

  static Value boundValueWithMax(const meta::cpp2::ColumnDef& col);

  static Value boundValueWithMin(const meta::cpp2::ColumnDef& col);

  static Value normalizeValue(const meta::cpp2::ColumnDef& col, const Value& v);

  static Status boundValue(Expression::Kind kind,
                           const Value& val,
                           const meta::cpp2::ColumnDef& col,
                           Value& begin,
                           Value& end);

  static void eraseInvalidIndexItems(
      int32_t schemaId, std::vector<std::shared_ptr<nebula::meta::cpp2::IndexItem>>* indexItems);

  // Find optimal index according to filter expression and all valid indexes.
  //
  // For relational condition expression:
  //   1. iterate all indexes
  //   2. select the best column hint for each index
  //     2.1. generate column hint according to the first field of index
  //
  // For logical condition expression(only logical `AND' expression):
  //   1. same steps as above 1, 2
  //   2. for multiple columns combined index:
  //     * iterate each field of index
  //     * iterate each operand expression of filter condition
  //     * collect all column hints generated by operand expression for each index field
  //     * process collected column hints, for example, merge the begin and end values of
  //       range scan
  //   3. sort all index results generated by each index
  //   4. select the largest score index result
  //   5. process the selected index result:
  //     * find the first not prefix column hint and ignore all followed hints except first
  //       range hint
  //     * check whether filter conditions are used, if not, place the unused expression parts
  //       into column hint filter
  //
  // For logical `OR' condition expression, use above steps to generate
  // different `IndexQueryContext' for each operand of filter condition, nebula
  // storage will union all results of multiple index contexts
  static bool findOptimalIndex(
      const Expression* condition,
      const std::vector<std::shared_ptr<nebula::meta::cpp2::IndexItem>>& indexItems,
      bool* isPrefixScan,
      nebula::storage::cpp2::IndexQueryContext* ictx);

  static bool relExprHasIndex(
      const Expression* expr,
      const std::vector<std::shared_ptr<nebula::meta::cpp2::IndexItem>>& indexItems);

  static void copyIndexScanData(const nebula::graph::IndexScan* from, nebula::graph::IndexScan* to);
};

}  // namespace graph
}  // namespace nebula
#endif  // NEBULA_GRAPH_OPTIMIZER_OPTIMIZERUTILS_H_
